﻿using GMap.NET;
using GMap.NET.MapProviders;
using GMap.NET.WindowsForms;
using GMap.NET.WindowsForms.Markers;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WherePoint
{
    public partial class MainForm : Form
    {
        public readonly GMapOverlay _objects = new GMapOverlay("objects");  // 照片组
        public readonly GMapOverlay _markers = new GMapOverlay("markers");  // 单张照片


        public MainForm()
        {
            InitializeComponent();
            InitGrid();                 // 初始化表格


            if (!GMapControl.IsDesignerHosted) {
                // add your custom map db provider
                //GMap.NET.CacheProviders.MySQLPureImageCache ch = new GMap.NET.CacheProviders.MySQLPureImageCache();
                //ch.ConnectionString = @"server=sql2008;User Id=trolis;Persist Security Info=True;database=gmapnetcache;password=trolis;";
                //MainMap.Manager.SecondaryCache = ch;

                MainMap.ShowTileGridLines = false;
                GMapProviders.GoogleSatelliteMap.TryCorrectVersion = false;

                MainMap.MapProvider = GMapProviders.GoogleSatelliteMap;             // 默认地图
                MainMap.Position = new PointLatLng(24.355, 109.404);                // 地图中心点(北京)GPS坐标
                MainMap.MinZoom = GMapProviders.GoogleSatelliteMap.MinZoom;         // 地图最小比例
                MainMap.MaxZoom = GMapProviders.GoogleSatelliteMap.MaxZoom ?? 24;   // 地图最大比例
                MainMap.Zoom = 13;                                                  // 当前缩放等级
                MainMap.DragButton = MouseButtons.Left;                             // 鼠标平移键

                MainMap.MouseMove += MainMap_MouseMove;

                MainMap.Overlays.Add(_objects);
                MainMap.Overlays.Add(_markers);
            }
        }
        /// <summary>
        /// 初始化表格
        /// </summary>
        private void InitGrid()
        {
            outlookGridPoints.Columns.Add(new DataGridViewTextBoxColumn { Name = "TYPE", HeaderText = "类别", Visible = false });
            outlookGridPoints.Columns.Add(new DataGridViewTextBoxColumn() { Name = "NAME", HeaderText = "名称" });
            outlookGridPoints.Columns.Add(new DataGridViewTextBoxColumn() { Name = "FILE", HeaderText = "文件", Visible = false });
            outlookGridPoints.Columns.Add(new DataGridViewTextBoxColumn() { Name = "COORDINATE", HeaderText = "坐标", Visible = false, ValueType = typeof(double[]) });
        }
        /// <summary>
        /// 显示坐标
        /// </summary>
        private void MainMap_MouseMove(object sender, MouseEventArgs e)
        {
            PointLatLng pnew = MainMap.FromLocalToLatLng(e.X, e.Y);
            toolStripStatusLabelInfo.Text = string.Format("经度:{0:0.000000} 纬度:{1:0.000000}", pnew.Lng, pnew.Lat);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        private void OpenPath(string path)
        {
            DataTable table = MakeTable(path);
            if (table.Rows.Count == 0) return;
            DataSet dataset = new DataSet();
            dataset.Tables.Add(table);
            outlookGridPoints.BindData(dataset, table.TableName);
            outlookGridPoints.GroupTemplate.Column = outlookGridPoints.Columns["TYPE"];
            outlookGridPoints.Sort(new DataRowComparerEx(0, ListSortDirection.Ascending));

            foreach (DataColumn col in table.Columns) {
                outlookGridPoints.Columns[col.ColumnName].ValueType = col.DataType;
            }
            outlookGridPoints.Columns["TYPE"].Visible = false;
            outlookGridPoints.Columns["NAME"].Visible = true;
            outlookGridPoints.Columns["FILE"].Visible = false;
            outlookGridPoints.Columns["COORDINATE"].Visible = false;

            outlookGridPoints.Columns["TYPE"].HeaderText = "分类";
            outlookGridPoints.Columns["NAME"].HeaderText = "名称";
        }
        /// <summary>
        /// 解析目录数据
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        private DataTable MakeTable(string path)
        {
            // 解析深度2级
            DataTable result = new DataTable("照片");
            result.Columns.Add("TYPE", typeof(string));         // 分类
            result.Columns.Add("NAME", typeof(string));         // 文件名
            result.Columns.Add("FILE", typeof(string));         // 文件
            result.Columns.Add("COORDINATE", typeof(double[])); // 坐标

            StreamWriter writer = new StreamWriter("POINTS.TXT", false);
            int num = 0;
            // 解析第一级
            string[] rootfiles = System.IO.Directory.GetFiles(path, "*.jpg");
            foreach (string file in rootfiles) {
                double lat, lng;
                bool success = GetData(file, out lat, out lng);
                if (success == false) continue;
                DataRow row = result.NewRow();
                row["TYPE"] = "无分类";
                row["NAME"] = System.IO.Path.GetFileNameWithoutExtension(file);
                row["FILE"] = file;
                row["COORDINATE"] = new double[] { lat, lng };
                result.Rows.Add(row);

                // 标记中心点
                GMapMarker marker = new GMarkerGoogle(new PointLatLng(lat, lng), GMarkerGoogleType.lightblue);
                marker.ToolTipMode = MarkerTooltipMode.Always;
                marker.ToolTipMode = MarkerTooltipMode.OnMouseOver;
                marker.ToolTipText = System.IO.Path.GetFileNameWithoutExtension(file);
                _objects.Markers.Add(marker);
                writer.WriteLine("{0},{1:0.000000000000},{2:0.000000000000}", num++, lat, lng);
            }
            writer.Flush();
            writer.Close();
            // 解析第二级
            string[] rootdirs = System.IO.Directory.GetDirectories(path);
            //_objects.Markers.Clear();
            foreach (string rootdir in rootdirs) {
                string name = new System.IO.DirectoryInfo(rootdir).Name.ToUpper();
                string[] files = System.IO.Directory.GetFiles(rootdir, "*.jpg");

                List<double[]> points = new List<double[]>();
                foreach (string file in files) {
                    double lat, lng;
                    bool success = GetData(file, out lat, out lng);
                    if (success == false) continue;
                    DataRow row = result.NewRow();
                    row["TYPE"] = name;
                    row["NAME"] = System.IO.Path.GetFileNameWithoutExtension(file);
                    row["FILE"] = file;
                    row["COORDINATE"] = new double[] { lat, lng };
                    result.Rows.Add(row);
                    points.Add(new double[] { lat, lng });
                }
                if (points.Count > 0) {
                    double[] centerpoint = GetCenterPoint(points);      // 计算中心点
                    Console.Write("{0}\t{1:0.000000}\t{2:0.000000}\r\n", name, centerpoint[0], centerpoint[1]);
                    // 标记中心点
                    GMapMarker marker = new GMarkerGoogle(new PointLatLng(centerpoint[0], centerpoint[1]), GMarkerGoogleType.lightblue);
                    marker.ToolTipMode = MarkerTooltipMode.Always;
                    marker.ToolTipText = name;
                    _objects.Markers.Add(marker);
                }

            }
            return result;
        }
        /// <summary>
        /// 解析图片信息
        /// </summary>
        /// <param name="file"></param>
        /// <param name="lat"></param>
        /// <param name="lng"></param>
        /// <returns></returns>
        private bool GetData(string file, out double lat, out double lng)
        {
            try {
                double[] lats, lngs;
                
                using (ExifReader reader = new ExifReader(file)) {
                    bool success1 = reader.GetTagValue(ExifTags.GPSLatitude, out lats);
                    bool success2 = reader.GetTagValue(ExifTags.GPSLongitude, out lngs);
                }
                lat = lats[0] + lats[1] / 60.0 + lats[2] / 3600.0;
                lng = lngs[0] + lngs[1] / 60.0 + lngs[2] / 3600.0;

                //Bitmap img = (Bitmap)Bitmap.FromFile(file);
                //using (ExifManager exifMan = new ExifManager(img)) {
                //    lat = exifMan.Latitude;
                //    lng = exifMan.Longitude;
                //}
                return true;
            }
            catch (Exception ex) { lat = lng = 0; return false; }

        }
        /// <summary>
        /// 显示照片位置
        /// </summary>
        private void outlookGridPoints_SelectionChanged(object sender, EventArgs e)
        {
            // 
            _markers.Markers.Clear();
            if (outlookGridPoints.SelectedRows.Count != 1) return;

            string name = outlookGridPoints.SelectedRows[0].Cells["NAME"].Value as string;
            if (name == null) return;
            string type = outlookGridPoints.SelectedRows[0].Cells["TYPE"].Value as string;
            string file = outlookGridPoints.SelectedRows[0].Cells["FILE"].Value as string;

            double[] coordinate = (double[])outlookGridPoints.SelectedRows[0].Cells["COORDINATE"].Value;
            if (coordinate == null || coordinate.Length != 2) return;
            double lat = coordinate[0];
            double lng = coordinate[1];

            GMapMarker marker = new GMarkerGoogle(new PointLatLng(lat, lng), GMarkerGoogleType.pink);
            marker.ToolTipMode = MarkerTooltipMode.Always;
            marker.ToolTipText = string.IsNullOrEmpty(type) ? name : string.Format("({0}) {1}", type, name);
            _markers.Markers.Add(marker);

            pictureBoxPhoto.Image = null;
            pictureBoxPhoto.ImageLocation = null;
            if (System.IO.File.Exists(file)) pictureBoxPhoto.ImageLocation = file;
        }
        /// <summary>
        /// 获取中心点
        /// </summary>
        /// <param name="points"></param>
        /// <returns></returns>
        private double[] GetCenterPoint(List<double[]> points)
        {
            // 判断有效点 距离在50M以内的都为有效点
            bool[] flags = new bool[points.Count];
            for (int m = 0; m < points.Count - 1; m++) {
                for (int n = m + 1; n < points.Count; n++) {
                    double distance = GetDistance(points[m][1], points[m][0], points[n][1], points[n][0]);
                    if (distance <= 50) {
                        flags[m] = flags[n] = true;
                        break;
                    }
                }
            }
            // 
            double lat = 0;
            double lng = 0;
            int count = 0;
            for (int i = 0; i < points.Count; i++) {
                if (flags[i] == false) continue;
                lat += points[i][0];
                lng += points[i][1];
                count++;
            }
            if (count == 0) {
                lat = points[0][0];
                lng = points[0][1];
            }
            else {
                lat = lat / (double)count;
                lng = lng / (double)count;
            }
            return new double[] { lat, lng };
        }

        private const double EARTH_RADIUS = 6378137;   // 赤道半径(单位m)  
        /// <summary>
        /// 转化为弧度(rad) 
        /// </summary>
        /// <param name="d"></param>
        /// <returns></returns>
        private double rad(double d)
        {
            return d * Math.PI / 180.0;
        }

        #region 算法1
        /// <summary>
        /// 基于余弦定理求两经纬度距离
        /// </summary>
        /// <param name="lon1">第一点的经度</param>
        /// <param name="lat1">第一点的纬度</param>
        /// <param name="lon2">第二点的经度</param>
        /// <param name="lat2">第二点的纬度</param>
        /// <returns>返回的距离，单位km</returns>
        private double LantitudeLongitudeDist(double lon1, double lat1, double lon2, double lat2)
        {
            double radLat1 = rad(lat1);
            double radLat2 = rad(lat2);
            double radLon1 = rad(lon1);
            double radLon2 = rad(lon2);

            if (radLat1 < 0) radLat1 = Math.PI / 2 + Math.Abs(radLat1); // south
            if (radLat1 > 0) radLat1 = Math.PI / 2 - Math.Abs(radLat1); // north
            if (radLon1 < 0) radLon1 = Math.PI * 2 - Math.Abs(radLon1); // west
            if (radLat2 < 0) radLat2 = Math.PI / 2 + Math.Abs(radLat2); // south
            if (radLat2 > 0) radLat2 = Math.PI / 2 - Math.Abs(radLat2); // north
            if (radLon2 < 0) radLon2 = Math.PI * 2 - Math.Abs(radLon2); // west

            double x1 = EARTH_RADIUS * Math.Cos(radLon1) * Math.Sin(radLat1);
            double y1 = EARTH_RADIUS * Math.Sin(radLon1) * Math.Sin(radLat1);
            double z1 = EARTH_RADIUS * Math.Cos(radLat1);

            double x2 = EARTH_RADIUS * Math.Cos(radLon2) * Math.Sin(radLat2);
            double y2 = EARTH_RADIUS * Math.Sin(radLon2) * Math.Sin(radLat2);
            double z2 = EARTH_RADIUS * Math.Cos(radLat2);

            double d = Math.Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) + (z1 - z2) * (z1 - z2));
            // 余弦定理求夹角  
            double theta = Math.Acos((EARTH_RADIUS * EARTH_RADIUS + EARTH_RADIUS * EARTH_RADIUS - d * d) / (2 * EARTH_RADIUS * EARTH_RADIUS));
            double dist = theta * EARTH_RADIUS;
            return dist;
        }
        #endregion

        #region 算法2
        /// <summary>
        /// 基于googleMap中的算法得到两经纬度之间的距离,计算经度与谷歌地图的距离精度差不多，相差范围在0.2米以下
        /// </summary>
        /// <param name="lon1">第一点的经度</param>
        /// <param name="lat1">第一点的纬度</param>
        /// <param name="lon2">第二点的经度</param>
        /// <param name="lat2">第二点的纬度</param>
        /// <returns>返回的距离，单位km</returns>
        private double GetDistance(double lon1, double lat1, double lon2, double lat2)
        {
            double radLat1 = rad(lat1);
            double radLat2 = rad(lat2);
            double a = radLat1 - radLat2;
            double b = rad(lon1) - rad(lon2);
            double s = 2 * Math.Asin(Math.Sqrt(Math.Pow(Math.Sin(a / 2), 2) + Math.Cos(radLat1) * Math.Cos(radLat2) * Math.Pow(Math.Sin(b / 2), 2)));
            s = s * EARTH_RADIUS;
            return s;
        }
        #endregion

        /// <summary>
        /// 双击打开
        /// </summary>
        private void pictureBoxPhoto_DoubleClick(object sender, EventArgs e)
        {
            string file = pictureBoxPhoto.ImageLocation;
            if (System.IO.File.Exists(file)) {
                System.Diagnostics.Process.Start(file);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        private void toolStripButtonOpen_Click(object sender, EventArgs e)
        {
            FolderBrowserDialogEx dialog = new FolderBrowserDialogEx();
            if (dialog.ShowDialog(this) != System.Windows.Forms.DialogResult.OK) return;
            OpenPath(dialog.SelectedPath);

        }
        /// <summary>
        /// 导出坐标
        /// </summary>
        private void toolStripButtonExport_Click(object sender, EventArgs e)
        {
            SaveFileDialog dialog = new SaveFileDialog();
            dialog.Filter = "坐标信息(*.TXT)|*.TXT";
            if (dialog.ShowDialog(this) != DialogResult.OK) return;

            int num = 0;
            StreamWriter writer = new StreamWriter(dialog.FileName, false);
            writer.WriteLine("ID\tLAT\tLNG\tNAME");
            // 对名称进行排序
            foreach (GMapMarker marker in _objects.Markers.OrderBy(m => m.ToolTip)) {
                double lat = marker.Position.Lat;
                double lng = marker.Position.Lng;
                string name = marker.ToolTipText;
                writer.WriteLine("{0}\t{1:0.00000000}\t{2:0.00000000}\t{3}", num++, lat, lng, name);
            }
            writer.Flush();
            writer.Close();
            MessageBox.Show("完成");
        }

        private void toolStripButtonCollect_Click(object sender, EventArgs e)
        {
            FolderBrowserDialogEx dialog = new FolderBrowserDialogEx();
            if (dialog.ShowDialog() != System.Windows.Forms.DialogResult.OK) return;
            string[] files = System.IO.Directory.GetFiles(dialog.SelectedPath, "*.jpg", SearchOption.AllDirectories);
            List<string> list = new List<string>(files);
            list.Sort();

            for (int i = 0; i < list.Count; i++) {
                System.IO.File.Copy(files[i], i.ToString("00000") + ".JPG");
            }

        }
    }
}
